#include "cmsis_os.h" 
#include "stm32f4xx.h" 
#include "GPIO_STM32F4xx.h"             // Keil::Device:GPIO
#include "rl_fs.h"                      /* FileSystem definitions             */
#include <stdio.h>                      /* Standard I/O .h-file               */
#include <ctype.h>                      /* Character functions                */
#include <string.h>                     /* String and memory functions        */
#include "Driver_MCI.h"                 // ::Drivers:MCI

#include "fsfunctions.h"  // funkcii po rabote s failovoi sistemoi
#include "FIR.h"          // raschet koefficientov filtra
#include "Detector.h"     // STALTA detector
//Include files for DSP library
#include "arm_math.h"


#define BLOCK_SIZE      4096 // chislo elementov v odnom bloke dannyh
#define NUM_TAPS        32   // chislo koefficientov filtra
#define FREQUENCY 400000.0f  // chastota discretizacii
#define INTERVALS_COUNT 64   // kolichestv intervalov 

uint32_t blockSize = BLOCK_SIZE;
static q15_t firState[BLOCK_SIZE + NUM_TAPS - 1]; // massiv sostoyanii filtra
static q15_t firCoef[NUM_TAPS];                   // massiv koefficientov filtra
arm_fir_instance_q15 FIRinstance;	                // sobstvenno filtr
q15_t inputBuffer[BLOCK_SIZE];                 
q15_t outputBuffer[BLOCK_SIZE];
FIR fir;           // ob'ekt dla rascheta koefficientov filtra
Detector detector; // ob'ekt detectora
FILE *f1, *f2, *f3;
char *input_fn = "test1.dat";
char *output_fn = "output.dat";
char *results_fn = "results.txt";
char readLineBuf[256];
unsigned int k = 0, j = 0;  
unsigned long i;
unsigned int index = 0;
float stalta, threshold, cs;
unsigned long long ticks = 0; // kolichestvo tickov
unsigned int imp = 0;   // kolichestvo naidennyh impulsov
unsigned long long beginning, ending;
unsigned short amplitude, maxAmplitude = 0;
unsigned short ampThreshold = 1200;  // eto znechenie dolzhno vvoditsya s ekrana
unsigned int areaThreshold = 120000; // eto znechenie dolzhno vvoditsya s ekrana
unsigned int area,  maxArea = 0;
unsigned int intervals[INTERVALS_COUNT];
unsigned short intervalNum;
unsigned int Na1, Na2, N015, b;
unsigned long long Tf;


static void init_pins (void) {
	GPIO_PortClock(GPIOA, ENABLE);
	GPIO_PortClock(GPIOB, ENABLE);
	GPIO_PinConfigure(GPIOA,12,GPIO_MODE_OUTPUT,GPIO_OUTPUT_PUSH_PULL, GPIO_OUTPUT_SPEED_100MHz,GPIO_NO_PULL_UP_DOWN);
	GPIO_PinConfigure(GPIOB,14,GPIO_MODE_OUTPUT,GPIO_OUTPUT_PUSH_PULL, GPIO_OUTPUT_SPEED_100MHz,GPIO_NO_PULL_UP_DOWN);
	GPIO_PinConfigure(GPIOB,15,GPIO_MODE_OUTPUT,GPIO_OUTPUT_PUSH_PULL, GPIO_OUTPUT_SPEED_100MHz,GPIO_NO_PULL_UP_DOWN);	
	GPIO_PinWrite(GPIOB,14,1);
	osDelay(100);
	GPIO_PinWrite(GPIOB,15,1);
	osDelay(2000);
}

static void pingLED(uint32_t delay, uint32_t times) {
	uint32_t t;
	for (t = 0; t < times; t++)	{
		GPIO_PinWrite(GPIOA,12,1);		
    osDelay(delay);  
    GPIO_PinWrite(GPIOA,12,0);	
		osDelay(delay); 
	}
}

static void OnLED()  { GPIO_PinWrite(GPIOA,12,1);	}
static void OffLED() { GPIO_PinWrite(GPIOA,12,0);	}

/* obrabotka odnogo otscheta ADC */
void Step(short sample) {
	if (index == blockSize - 1) { // nakopilsya celyi block dannyh
		index = 0;
		arm_fir_q15(&FIRinstance, inputBuffer, outputBuffer, blockSize); // filtruem
	  //fwrite(outputBuffer, sizeof(q15_t), blockSize, f2);  // pishem na flash karty
		for (k = 0; k < blockSize; k++) { 
			if (outputBuffer[k] < 0) outputBuffer[k] = -outputBuffer[k]; // berem znachenie po modulu
			stalta = STALTA(&detector, outputBuffer[k]);
			if (!detector.begFound) {
				detector.cs = 0;
				threshold = Threshold(&detector, outputBuffer[k]);
				if (stalta >= threshold) { // nashli nachalo impulsa
					detector.begFound = true;
					beginning = ticks;
					area = outputBuffer[k];
					amplitude = outputBuffer[k];
			  }
			}
			else {
				area += outputBuffer[k];
				if (amplitude < outputBuffer[k]) amplitude = outputBuffer[k];
				cs = CS(&detector, stalta);
				if (cs <= 0) { // nashli konec impulsa
					detector.begFound = false;
					imp++;
					ending = ticks;
					if (maxAmplitude < amplitude) maxAmplitude = amplitude;
					if (maxArea < area) maxArea = area;
					if (f3 != NULL) {
						// pishem v fail: nomer, nachalo, konec, amplituda, ploshad'
						fprintf(f3, "%u\t%llu\t%llu\t%u\t%u\n", imp, beginning, ending, amplitude, area);
						fflush (f3);
					}
			  }
			}
			ticks++;
		}
		inputBuffer[index] = sample;
	}
	else {
		inputBuffer[index] = sample;
		index++;
	}	
}

/* obrabotka resul'tatov */
void ProcessResultsByAmplitude(bool automatic) {
	f3 = fopen(results_fn, "r");
	if (f3 == NULL) {
    return;
  }	
	if (automatic)	
		ampThreshold = maxAmplitude / 2; // v avtomaticheskom rejime stavim porog amplitudy kak polovina maksimal'noi
	
	while (fscanf(f3, "%u%llu%llu%hu%u", &imp, &beginning, &ending, &amplitude, &area) == 5) {	
		intervalNum = amplitude/(maxAmplitude/INTERVALS_COUNT);
		if (intervalNum >= INTERVALS_COUNT) intervalNum = INTERVALS_COUNT - 1;
		intervals[intervalNum]++;
		if (amplitude <= ampThreshold) 
			Na1++;
		else
			Na2++;	
	}
	b = Na1 / Na2;
	Tf = ticks / FREQUENCY / 15;
	N015 = Na1 / Tf;
	fclose (f3);
}

void ProcessResultsByArea(bool automatic) {
	f3 = fopen(results_fn, "r");
	if (f3 == NULL) {
    return;
  }	
	if (automatic)
		areaThreshold = maxArea / 2; // v avtomaticheskom rejime stavim porog ploshadi kak polovina maksimal'noi
	while (fscanf(f3, "%u%llu%llu%hu%u", &imp, &beginning, &ending, &amplitude, &area) == 5) {	
		intervalNum = area/(maxArea/INTERVALS_COUNT);
		if (intervalNum >= INTERVALS_COUNT) intervalNum = INTERVALS_COUNT - 1;
		intervals[intervalNum]++;
		if (area <= areaThreshold) 
			Na1++;
		else
			Na2++;	
	}
	b = Na1 / Na2;
	Tf = ticks / FREQUENCY / 15;
	N015 = Na1 / Tf;
	fclose (f3);
}

int main (void) {
	SystemInit();
	init_pins();       // inicializaciya pinov
	init_filesystem(); // inicializaciya failovoi sistemy
	DetectorInit(&detector, 0.001f, 0.02f, FREQUENCY); // inicializaciya detectora 
	
	/* otkrivaem faily na chtenie i na zapis' */
  f1 = fopen(input_fn, "rb"); 
  if (f1 == NULL) {
    return 1;
  }
	f2 = fopen(output_fn, "wb");
	if (f2 == NULL) {
    return 1;
  }
	f3 = fopen(results_fn, "wb");
	if (f3 == NULL) {
    return 1;
  }
	
	/* inicializaciya fil'tra i raschet ego koefficientov */ 
	FirInit(&fir, 4, 5, NUM_TAPS, 1000, 100000, FREQUENCY);
	memcpy(firCoef, fir.coef, NUM_TAPS*sizeof(firCoef[0]));
	arm_fir_init_q15(&FIRinstance, NUM_TAPS, (q15_t *)&firCoef[0], firState, blockSize);
	FirFree(&fir);

	/* neskol'ko holostyh ciklov, chtoby ustakanilis' znacheniya detectora i poroga */
	detector.cs = 0;
	for (k = 0; k < 250; k++) {
		fread(inputBuffer, sizeof(short), blockSize, f1);
		arm_fir_q15(&FIRinstance, inputBuffer, outputBuffer, blockSize); // filtruem
		for (i = 0; i < blockSize; i++) { // imitiruem prihod po odnomu otschetu s ADC
			if (outputBuffer[k] < 0) outputBuffer[k] = -outputBuffer[k];
		  stalta    = STALTA(&detector, outputBuffer[k]);
		  threshold = Threshold(&detector, outputBuffer[k]);
		}	
	}
	
	OnLED();
	while (fread(inputBuffer, sizeof(short), blockSize, f1) == blockSize) { // chitaem blokami
		for (i = 0; i < blockSize; i++) { // imitiruem prihod po odnomu otschetu s ADC
		  Step(inputBuffer[i]);			
		}
	}
	OffLED();
	fclose (f1);
  fclose (f2);
	fclose (f3);
	
//	ticks = 0x1f52000;
//	maxAmplitude = 2589;
//	maxArea = 239413;
//	
//	f2 = fopen(output_fn, "r");
//	if (f2 == NULL) {
//    return 1;
//  }
//	f3 = fopen(results_fn, "r");
//	if (f3 == NULL) {
//    return 1;
//  }	
//	while (fscanf(f3, "%u%llu%llu%hu%u", &imp, &beginning, &ending, &amplitude, &area) == 5) {	
//		fseek(f2, beginning*2, SEEK_SET);
//		amplitude = 0;
//		while (beginning <= ending) {
//			beginning++;
//			fread(inputBuffer, sizeof(short), 1, f2);
//			if (inputBuffer[0] < 0) inputBuffer[0] = -inputBuffer[0];
//			if (amplitude < inputBuffer[0]) amplitude = inputBuffer[0];
//		}
//	}
	
	ProcessResultsByAmplitude(true);
	ProcessResultsByArea(true);
	uninit_filesystem();
}
